import axios from 'axios'
import { ElMessage } from 'element-plus'
import { Session } from '/@/utils/storage'
import useLoading from '/@/hooks/loading'
import qs from 'qs'
import { getBaseApiUrl } from './base-url'
import { autoLogin } from './login'
import { downloadFile } from './download'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
  baseURL: getBaseApiUrl(),
  timeout: 60000,
  headers: { 'Content-Type': 'application/json' },
  paramsSerializer: {
    serialize(params) {
      return qs.stringify(params, { allowDots: true })
    }
  }
})

// 添加请求拦截器
service.interceptors.request.use(
  config => {
    useLoading(true)
    // 在请求头插入时间戳, 避免缓存
    config.headers!.t = Date.now()

    return config
  },
  error => {
    useLoading(false)
    // 对请求错误做些什么
    return Promise.reject(error)
  }
)

// 添加响应拦截器
service.interceptors.response.use(
  response => {
    let res = {
      success: true,
      status: response.status,
      code: '0',
      data: ''
    } as AxiosResponse
    switch (true) {
      case response.data instanceof Blob:
      case response.data instanceof ArrayBuffer: {
        downloadFile(response)
        break
      }

      default: {
        // 对响应数据做点什么
        res = response.data
        res.status = response.status
        res.success = res.code === '0'

        break
      }
    }
    useLoading(false)
    return res
  },
  async error => {
    // 对响应错误做点什么
    const res = {
      status: 0,
      success: false,
      msg: '',
      data: '',
      code: '-1'
    } as AxiosResponse
    let data
    if (error.response) {
      res.status = error.response.status
      // 有成功连接服务器并有数据返回
      switch (res.status) {
        case 400:
        case 403:
        case 500: {
          // 如果后端有正常返回数据
          switch (true) {
            case error.response.data instanceof Blob: {
              // 不是很正常的数据
              data = await error.response.data.text()
              data = JSON.parse(data)
              res.msg = data.msg || '未知错误'
              res.code = data.code
              res.data = data.data
              break
            }
            case error.response.data instanceof ArrayBuffer: {
              // 这种情况一般是下载出错需要转换成字符串
              data = JSON.parse(new TextDecoder().decode(error.response.data))
              res.msg = data.msg || '未知错误'
              res.code = data.code
              res.data = data.data
              break
            }
            case typeof error.response.data === 'object' &&
              'code' in error.response.data: {
              const { data: resdata } = error.response
              // 正常返回的数据
              res.msg = resdata.msg || '未知错误'
              res.code = resdata.code
              res.data = resdata.data
              break
            }
            default: {
              res.msg = `${error.message} ${error.config.url}`
              break
            }
          }
          break
        }
        case 401: {
          Session.clear() // 清除浏览器全部临时缓存
          autoLogin() // 跳转登录
          return
        }
        // case 403:
        //   // 处理403无权限错误
        //   if (window.location.pathname !== '/403') {
        //     window.location.href = '/403'
        //   }
        // return
        case 404: {
          res.msg = `404 Not Found ${error.config.url}`
          break
        }
        default: {
          res.msg = `${res.status} An error occurred ${error.config.url}`
          break
        }
      }
    } else {
      // 没有连接到服务器的情况
      switch (true) {
        case error.message === 'Network Error': {
          res.msg = '网络连接错误'
          break
        }
        case error.message.includes('timeout'): {
          res.msg = '网络超时'
          break
        }
        default: {
          res.msg = error.message
          break
        }
      }
    }

    if (error.config.showError !== false) {
      ElMessage.error(res.msg)
    }
    useLoading(false)
    // 统一返回数据格式
    return res
  }
)

/**
 * 获取请求的对象
 * @param path 接口路径
 * @returns 请求函数
 */
const useRequest = (path: string) => {
  /**
   * 请求主体方法
   * @param method 请求方法
   * @param functionName 接口名
   * @param options axios options
   * @returns axios promise
   */
  const request = <T>(
    method: string,
    functionName: string,
    options: AxiosRequestConfig = {}
  ) => {
    return service<T>({
      method,
      url: `${path}${functionName}`,
      ...options
    }) as Promise<ResultVO<T>>
  }

  /**
   * get
   * @param functionName 接口名
   * @param params 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const get = <T>(
    functionName: string,
    params: any,
    options: AxiosRequestConfig = {}
  ) => {
    return request<T>('get', functionName, {
      params,
      ...options
    })
  }

  /**
   * post
   * @param functionName 接口名
   * @param data 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const post = <T>(
    functionName: string,
    data: any,
    options: AxiosRequestConfig = {}
  ) => {
    return request<T>('post', functionName, {
      data,
      ...options
    })
  }

  /**
   * delete
   * @param functionName 接口名
   * @param params 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const deleteMethod = <T>(
    functionName: string,
    params: any,
    options: AxiosRequestConfig = {}
  ) => {
    return request<T>('post', functionName, {
      params,
      ...options
    })
  }

  /**
   * put
   * @param functionName 接口名
   * @param data 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const put = (
    functionName: string,
    data: any,
    options: AxiosRequestConfig = {}
  ) => {
    return request('post', functionName, {
      data,
      ...options
    })
  }

  /**
   * download
   * @param method 请求方法
   * @param functionName 接口名
   * @param data 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const download = <T>(
    method: string,
    functionName: string,
    data: any,
    options: AxiosRequestConfig = {}
  ) => {
    const config = {
      // 添加返回数据类型
      responseType: 'arraybuffer',
      ...options
    } as AxiosRequestConfig
    switch (method) {
      case 'post':
      case 'put': {
        config.data = data
        break
      }
      case 'get':
      case 'delete': {
        config.params = data
        break
      }
    }

    return request<T>(method, functionName, config)
  }

  /**
   * upload
   * @param functionName 接口名
   * @param data 请求数据
   * @param options axios options
   * @returns axios promise
   */
  const upload = <T>(
    functionName: string,
    data: Record<string, any>,
    options: AxiosRequestConfig = {}
  ) => {
    // 用formData组装数据
    const formData = new FormData()
    for (const key in data) {
      formData.append(key, data[key])
    }
    options.headers = {
      'content-type': 'application/form-data'
    }
    return request<T>('post', functionName, {
      data: formData,
      ...options
    })
  }

  return {
    request,
    get,
    post,
    delete: deleteMethod,
    put,
    download,
    upload
  }
}

// 导出 axios 实例
export default useRequest

export { service }
